#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2024 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import requests
import json
import os
import subprocess
import shutil
import sys
from innerapi_check import check_build_parts
import traceback
from precise.replace_rely import replace_rely_so
import argparse
from read_matrix_product_csv import get_project_indep_compile_support_status
import time


def _run_shell_script(script, args=None, check=True, env=None):
    """
    执行shell脚本
    :param script_name: 脚本名称
    :param args: 脚本参数
    :param check: 是否检查脚本执行结果
    :param env: 环境变量
    """
    if args is None:
        args = []
    script_path = os.path.join(os.getcwd(), script)
    if not os.path.exists(script_path):
        raise FileNotFoundError(f"Script {script} does not exist.")
    if not env:
        env = os.environ.copy()
    os.chmod(script_path, 0o755)
    bash_path = "/bin/bash"
    subprocess.run([bash_path, script_path] + args, check=check, env=env)


def print_basic_info(file_list, project_info, support_status):
    data = {
        "project_info": project_info,
        "file_list": file_list,
        "indep_surpport_status": support_status
    }
    json_str = json.dumps(data)
    with open("data.json", "w") as f:
        f.write(json_str)
    print("basic info:")
    with open("data.json", "r") as f:
        print(f.read())
    return data


def _get_project_list(export):
    list_export = []
    if export == "project_list":
        export_button = os.getenv(export)
        if export_button == None:
            print("The environment variable project_list is none")
            return list_export
        list_export += export_button.split(",")
        return list_export


def _get_modified_files(export):
    if export == "PR_FILE_PATHS":
        export_button = os.getenv(export)
        if export_button == None:
            print("The environment variable PR_FILE_PATHS is none")
        if isinstance(export_button, str):
            export_button = json.loads(export_button)
        return export_button
    # structure of export_button
    # {
    #     "projuct_name": [
    #         "file1",
    #         "file2",
    #         ...
    #     ],
    #     "project_name2": [
    #         "file1",
    #         "file2",
    #         ...
    #     ]
    # }


def _get_project_detail(param):
    post_url = "http://ci.openharmony.cn/api/sple/external/artifact/repo?projectName=openharmony"
    fails = 0
    while True:
        try:
            if fails >= 5:
                break
            headers = {"Content-Type": "application/json"}
            ret = requests.post(
                post_url, data=param, headers=headers, verify=False, timeout=10
            )
            if ret.status_code == 200:
                project_detail = json.loads(ret.text)
            else:
                continue
        except Exception as e:
            print(e)
            fails += 1
            print("Network connection issue, attempting to request again : ", fails)
        else:
            break
        # structure of project_detail
        # {
        #     "projectName": {
        #         "partName": "partPath",
        #         "partName2": "partPath2",
        #         ...
        #     },
        #      "projectName2": {
        #         "partName": "partPath",
        #         "partName2": "partPath2",
        #         ...
        #     }
        # }
    return project_detail


def _get_build_parts(project_info_list, file_list_dict):
    part = []
    for project_name, project_info in project_info_list.items():
        # 项目内无部件信息
        if len(project_info) == 0:
            continue
        # 项目内只有一个部件信息，添加
        if len(project_info) == 1:
            part += list(project_info.keys())
        # 项目内有多个部件信息, 只添加修改所涉及的部件
        else:
            for inner_part_name, part_path in project_info.items():
                subsystem_name_index = part_path.find(project_name.split("_")[0])
                if subsystem_name_index == -1:
                    continue
                relative_path_in_project = part_path[subsystem_name_index + len(project_name) + 1 :]
                if Judgment(project_name, relative_path_in_project, file_list_dict):
                    part.append(inner_part_name)
    return part


def Judgment(project_name, relative_path_in_project, file_list_dict):
    if not file_list_dict or (project_name not in file_list_dict):
        return False
    for project_name, modified_files in file_list_dict.items():
        for file in modified_files:
            if file.startswith(relative_path_in_project):
                return True
    return False


def hb_and_pip_install():
    """
    执行hb和pip安装
    """
    script = "hb_and_pip_install.sh"
    _run_shell_script(script)


def copy_sdk():
    """ 
    执行同步代码和预编译脚本, 包括sdk下载
    """
    script = "copy_sdk.sh"
    _run_shell_script(script)


def sync_code(project_path_list):
    """ 
    执行同步代码和预编译脚本, 包括sdk下载
    """
    script = "sync_code.sh"
    print("=======================")
    print(project_path_list)
    print("=======================")
    _run_shell_script(script, project_path_list)


def run_hb_build(build_parts:list, args):
    """
    执行hb build命令
    """
    script = "hb_build.sh"
    build_type = args.build_type
    if build_type == "i":
        build_type = ["-i"]
    elif build_type == "t":
        build_type = ["-t"]
    elif build_type == "it":
        build_type = ["-i", "-t"]
    hb_build = ["hb", "build"]
    hb_args = [f"--cpu {args.cpu}", f"--os {args.os}", f"--branch {args.branch}", f"--variant {args.variant}"]
    full_cmd = hb_build + build_parts + build_type + hb_args
    _run_shell_script(script, full_cmd)


def generate_component_package(build_parts, args):
    """"
    生成组件二进制包
    """
    cmd = "python build/templates/common/generate_component_package.py".split()
    cmd.extend(["-op", f"out/{args.variant}/src"])
    cmd.extend(["-rp", "./"])
    cmd.extend(["-cl", ",".join(build_parts), "-lt", "1", "-origin", "gate"])
    script = "generate_binary_package.sh"
    _run_shell_script(script, cmd)


def backup_package_phone():
    """"
    备份package/phone目录(除了system文件夹)
    """
    script = "back_package_phone.sh"
    _run_shell_script(script, None, check=False)


def backup_binarys():
    """"
    备份~/.hpm目录
    """
    script = "backup_binarys.sh"
    _run_shell_script(script, None, check=False)


def repalce_so(parts, args):
    for part in parts:
        cmd = f"python ./precise/replace_part_sofile.py --part-name {part} --new-folder code --old-folder ../ --variant {args.variant}"
        subprocess.run(cmd, shell=True, check=True)


def make_image(parts):
    subprocess.run(
        "cp ./precise/regenerate_packages_images.sh ../;cd ../;chmod +777 regenerate_packages_images.sh;./regenerate_packages_images.sh",
        shell=True,
        check=True,
    )


def process_arkcompiler_project(project_list: list):
    # 定义 ArkCompiler 相关部件集合，字符串间缺少逗号，补充后用集合提升查找效率
    arkcompiler_parts = {
        "arkcompiler_runtime_core",
        "arkcompiler_ets_frontend",
        "arkcompiler_ets_runtime"
    }
    # 检查 project_list 与 arkcompiler_parts 是否有交集
    if set(project_list).intersection(arkcompiler_parts):
        # 将不在 project_list 中的 arkcompiler_parts 添加到 project_list
        project_list.extend(arkcompiler_parts - set(project_list))
    return project_list


def gen_part_path_dict(project_info_dict):
    part_path_dict = dict()
    for project in project_info_dict:
        for part_name in project_info_dict[project]:
            part_path_dict[part_name] = project_info_dict[project][part_name]
    return part_path_dict


def check_innerapi_change(build_part_list, args):
    try:
        innerapi_change_info = check_build_parts(args)
        print("innerapi change info:", innerapi_change_info)
        for part in build_part_list:
            part_info = innerapi_change_info.get(part)
            innerapi_changed = part_info.get("innerapi_changed")
            if innerapi_changed:
                print(f"{part} innerapi changed")
                return True
        return False
    except Exception as e:
        print(f"check innerapi change error: {e}")
        traceback.print_exc()
        return True


def check_all_project_support_indep_compile(project_info_dict, args):
    try:
        # 检查是否所有项目都在门禁上部署了独立编译
        all_project_support_indep, support_result = get_project_indep_compile_support_status(project_info_dict, args)
        if not all_project_support_indep:
            print("Not all projects support indep compile, exit indep compile process.")
            return False, support_result
        else:
            return True, support_result
    except Exception as e:
        print(f"Error checking indep compile support: {e}")
        traceback.print_exc()
        return False, {}


def replace_rely(args):
    try:
        replace_rely_so(os.path.dirname(os.path.abspath(__file__)), args)
    except Exception as e:
        print(f"replace_rely_so error: {e}")
        traceback.print_exc()


def run_dayu200_tdd():
    env = os.environ.copy()
    script = "dayu200_tdd.sh"
    _run_shell_script(script, None, check=True, env=env)


def parse_args():
    """解析命令行参数"""
    parser = argparse.ArgumentParser(description='参数解析器')
    parser.add_argument('--cpu', default='arm', help='CPU类型，默认值为arm')
    parser.add_argument('--os', default='ohos', help='操作系统类型，默认值为ohos')
    parser.add_argument('--branch', '-b', default='master', help='分支名称，默认值为master，可使用-b指定')
    parser.add_argument('--variant', '-v', default='standard', help='变体名称，默认值为standard，可使用-v指定')
    parser.add_argument('--build-type', default='i', choices=['i', 't', 'it'], help='编译类型，可选i（代表编译源码）、t（代表编译用例）、it（代表编译源码和用例），默认值为i，可使用--build-type指定')
    parser.add_argument('--skip-smoke', default=False, action="store_true" ,help='设定是否执行冒烟测试')
    parser.add_argument('--package-products', default=False, action="store_true" ,help='打包二进制产物')
    parser.add_argument('--backup-env', default=False, action="store_true" ,help='备份编译时所使用的二进制包和基础镜像')
    return parser.parse_args()


def main(args):
    global flag, dep_list, indep_list
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    # 获取当前编译工程列表
    print("start run indep build")
    project_list = _get_project_list("project_list")
    project_list = process_arkcompiler_project(project_list)
    # if "arkui_ace_engine" in project_list:
    #     print("arkui_ace_engine is in project, skip indep compilation.")
    #     sys.exit(0)
    # debug info
    # project_list = ['developtools_syscap_codec', 'third_party_cares'] 
    print("current project_list:", project_list)
    project_info_dict = _get_project_detail(json.dumps(project_list))
    # debug info
    # project_info_dict = {'developtools_syscap_codec': {'syscap_codec': 'developtools/syscap_codec'}, 'third_party_cares': {'cares': 'third_party/cares'}}

    print("current compiling products info:", project_info_dict)
    # 获取当前pr修改的文件信息
    file_list_dict = _get_modified_files("PR_FILE_PATHS")
    # debug info
    # file_list_dict = {"developtools_syscap_codec": ["src/common_method.c"]}
    print("current modified file list:", file_list_dict)

    # 如何是build仓提交的pr，那么找个其他部件验证独立编译基本功能是否正常
    if len(project_list) == 1 and "build" in project_list[0]:
        print(f"found pr in build repository: project list info is:", project_list)
        project_list = ["developtools_syscap_codec"]
        project_info_dict = {'developtools_syscap_codec': {'syscap_codec': 'developtools/syscap_codec'}}
        file_list_dict = {"developtools_syscap_codec": []}
        args.skip_smoke = True

    # 检查所有项目是否都支持独立编译
    all_project_support_indep, support_status = check_all_project_support_indep_compile(project_info_dict, args)

    # 项目内可能存在多个部件，根据修改的文件信息判断哪些部件需要进行独立编译
    parts = _get_build_parts(project_info_dict, file_list_dict)
    
    # 打印基础信息到data.json
    print_basic_info(file_list_dict, project_info_dict, support_status)

    part_path_list = []
    build_part_list = []
    run_indep_build = False
    is_innerapi_changed = True

    if all_project_support_indep:
        build_part_list = [item for item in parts if "build" not in item]
        part_path_dict = gen_part_path_dict(project_info_dict)
        for part in build_part_list:
            part_path_list.append(part_path_dict[part])

    if build_part_list:
        is_innerapi_changed = check_innerapi_change(build_part_list, args)
        run_indep_build =  not is_innerapi_changed

    if run_indep_build:
        print("part_path_list:", part_path_list)
        sync_code(part_path_list)
        copy_sdk()
        hb_and_pip_install()
        run_hb_build(build_part_list, args)

    if run_indep_build and args.build_type  != "t":
        if args.package_products:
            generate_component_package(build_part_list, args)
        if not args.skip_smoke:
            need_backup_part = ["ability_runtime", "graphic_2d", "window_manager"]
            build_part_ = build_part_list[0]
            if args.backup_env or (len(build_part_list) == 1 and build_part_ in need_backup_part):
                backup_package_phone()
                backup_binarys()
            repalce_so(build_part_list, args)
            # replace_rely(args)
            make_image(build_part_list)
    if not run_indep_build and args.build_type == "t":
        print("run_dayu200_tdd")
        run_dayu200_tdd()


if __name__ == "__main__":
    args = parse_args()
    sys.exit(main(args))